package longshu.srpc.client.impl;

import longshu.srpc.api.RpcRequest;
import longshu.srpc.api.RpcResponse;
import longshu.srpc.api.RpcService;
import longshu.srpc.client.RpcConnector;
import longshu.srpc.client.RpcProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 * SimpleRpcProxy JDK Proxy实现
 *
 * @author LongShu 2017/12/03
 */
public class SimpleRpcProxy implements RpcProxy, InvocationHandler {

    private RpcConnector rpcConnector;

    public SimpleRpcProxy(RpcConnector rpcConnector) {
        this.rpcConnector = rpcConnector;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T extends RpcService> T getProxy(Class<? extends RpcService> serviceInterface) {
        InvocationHandler handler = new SimpleRpcProxy(rpcConnector);
        Object instance = Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface}, handler);
        return (T) instance;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Class<?> declaringClass = method.getDeclaringClass();
        // Object里的方法
        if (Object.class == declaringClass) {
            String name = method.getName();
            if ("equals".equals(name)) {
                return proxy == args[0];
            } else if ("hashCode".equals(name)) {
                return System.identityHashCode(proxy);
            } else if ("toString".equals(name)) {
                return proxy.getClass().getName() + "@" +
                        Integer.toHexString(System.identityHashCode(proxy)) +
                        "$" + this.getClass().getName();
            } else {
                return method.invoke(proxy, args);
            }
        }

        RpcRequest rpcRequest = new RpcRequest().setRequestId(UUID.randomUUID().toString())
                .setServiceName(declaringClass.getName()).setMethodName(method.getName())
                .setParameterTypes(method.getParameterTypes()).setParameters(args);

        RpcResponse rpcResponse = rpcConnector.send(rpcRequest);
        if (!rpcResponse.isSuccess()) {
            throw new RuntimeException(String.valueOf(rpcResponse.getError()));
        }
        return rpcResponse.getResult();
    }

}
